package com.mapforge.io;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.mapforge.map.TileMap;
import com.mapforge.map.Tileset;

public class IOHandlerManager {
	private List<IOHandlerInterface<Tileset>> tilesetIOHandlers;
	private List<IOHandlerInterface<TileMap>> tileMapIOHandlers;
	private Set<TileMapFileWrapper> recentFileSet;
	private List<TileMapFileWrapper> recentFileList;
	private int oldIndex;
	public static final int MAX_RECENT_FILES = 10;
	private static IOHandlerManager singletonInstance;

	private IOHandlerManager() {
		this.tilesetIOHandlers = new ArrayList<IOHandlerInterface<Tileset>>();
		this.tileMapIOHandlers = new ArrayList<IOHandlerInterface<TileMap>>();
		this.recentFileList = new ArrayList<TileMapFileWrapper>();
		this.recentFileSet = new HashSet<TileMapFileWrapper>();
	}

	public boolean addRecentFile(File file, String mapName) {
		if (recentFileSet.size() < MAX_RECENT_FILES) {
			if (!recentFileSet.contains(file)) {
				recentFileSet.add(new TileMapFileWrapper(file, mapName));
				recentFileList.add(new TileMapFileWrapper(file, mapName));
				return true;
			} else {
				return false;
			}
		}
		// exceeded maximum recent files
		else {
			// remove oldest file
			TileMapFileWrapper file2 = recentFileList.remove(oldIndex);
			recentFileSet.remove(file2);
			// insert new file
			recentFileList.add(oldIndex, new TileMapFileWrapper(file, mapName));
			recentFileSet.add(new TileMapFileWrapper(file, mapName));
			oldIndex = (oldIndex + 1) % MAX_RECENT_FILES;
			return true;
		}
	}

	public boolean removeRecentFile(File file) {
		if (recentFileSet.contains(file)) {
			recentFileSet.remove(file);
			recentFileList.remove(file);
			return true;
		} else {
			return false;
		}
	}

	public static IOHandlerManager getHandle() {
		if (singletonInstance == null) {
			singletonInstance = new IOHandlerManager();
		}
		return singletonInstance;
	}

	public TileMap loadTileMap(File file) throws IOException {
		for (IOHandlerInterface<TileMap> io : tileMapIOHandlers) {
			if (io.canLoad(file)) {
				return io.read(file);
			}
		}
		throw new IOException("Uknown tilemap file type: \"" + file.getName()
				+ "\"");
	}

	public Tileset loadTileset(File file) throws IOException {
		for (IOHandlerInterface<Tileset> io : tilesetIOHandlers) {
			if (io.canLoad(file)) {
				return io.read(file);
			}
		}
		throw new IOException("Uknown tileset file type: \"" + file.getName()
				+ "\"");
	}

	public boolean containsTilesetIOHandler(
			IOHandlerInterface<Tileset> tilesetIOHandler) {
		return this.tilesetIOHandlers.contains(tilesetIOHandler);
	}

	public boolean containsTileMapIOHandler(
			IOHandlerInterface<TileMap> tilemapIOHandler) {
		return this.tileMapIOHandlers.contains(tilemapIOHandler);
	}

	public boolean addTilesetIOHandler(
			IOHandlerInterface<Tileset> tilesetIOHandler) {
		return this.tilesetIOHandlers.add(tilesetIOHandler);
	}

	public boolean addTileMapIOHandler(
			IOHandlerInterface<TileMap> tileMapIOHandler) {
		return this.tileMapIOHandlers.add(tileMapIOHandler);
	}

	public boolean removeTilesetIOHandler(
			IOHandlerInterface<Tileset> tilesetIOHandler) {
		return this.tilesetIOHandlers.remove(tilesetIOHandler);
	}

	public boolean removeTileMapIOhandler(
			IOHandlerInterface<TileMap> tilemapIOHandler) {
		return tileMapIOHandlers.remove(tilemapIOHandler);
	}

	public Iterator<IOHandlerInterface<Tileset>> tilesetIterator() {
		return this.tilesetIOHandlers.iterator();
	}

	public Iterator<IOHandlerInterface<TileMap>> tilemapIterator() {
		return this.tileMapIOHandlers.iterator();
	}

	private class TileMapFileWrapper {
		File file;
		String mapName;

		public TileMapFileWrapper(File file, String mapname) {
			this.mapName = mapname;
			this.file = file;
		}
	}
}